package modbus

import (
	"errors"

	"gitee.com/lossage/simplego/events"
)

//为具体协议制作的接口，提供给三个具体独写协议使用
type IModbusTask interface {
	//返回值：发送帧数据结构，返回帧数据结构
	ReadOnce(adress, command uint8, startReg uint16, regLen uint8, datas []uint16) (*ModbusModel, *ModbusModel, error)
	//获取接受监听事件，以注册
	GetReceiverEvent() *events.EventParam[[]byte]
	//获取发送监听事件，以注册
	GetSenderEvent() *events.EventParam[[]byte]
}

//发送接收监听结构基类，实现IModbusTask中两个监听接口函数。应用时将本类作为基类继承即可
type FrameListener struct {
	receiverEvent *events.EventParam[[]byte]
	senderEvent   *events.EventParam[[]byte]
}

func (_this *FrameListener) GetReceiverEvent() *events.EventParam[[]byte] {
	if _this.receiverEvent == nil {
		_this.receiverEvent = new(events.EventParam[[]byte])
	}
	return _this.receiverEvent
}
func (_this *FrameListener) GetSenderEvent() *events.EventParam[[]byte] {
	if _this.senderEvent == nil {
		_this.senderEvent = new(events.EventParam[[]byte])
	}
	return _this.senderEvent
}

func CreateModbusAction() IModbusAction {
	modbusAction := new(ModbusAction)
	return modbusAction
}

//基于不同的数据流方式，为Modbus提供发送与接收时的特殊处理方案
type IModbusAction interface {
	//提供发送数据的端口与方案
	SetWriter(writer *func([]byte) error)
	//提供读取数据的端口与方案
	SetReader(reader *func() ([]byte, error))
	//读一次数据
	ReadOnce(addr, command uint8, regNo uint16, regLength uint8) error
	//写一次数据
	WriteOnce(addr, command uint8, regNo uint16, buffer []uint16) error
	//获取发送帧
	GetSendFrame() *ModbusProtocol
	//获取接收帧
	GetReceiveFrame() *ModbusProtocol
}

//基于Modbus数据协议解析的方法，组建读写对象。继承IModbusAction
type ModbusAction struct {
	sendFrame    ModbusProtocol
	receiveFrame ModbusProtocol
	//接口SetWriter之后保存
	writer *func([]byte) error
	//接口SetReader之后保存
	reader *func() ([]byte, error)
}

func (_this *ModbusAction) GetSendFrame() *ModbusProtocol {
	return &_this.sendFrame
}

func (_this *ModbusAction) GetReceiveFrame() *ModbusProtocol {
	return &_this.receiveFrame
}

func (_this *ModbusAction) SetWriter(writer *func([]byte) error) {
	_this.writer = writer
}

func (_this *ModbusAction) SetReader(reader *func() ([]byte, error)) {
	_this.reader = reader
}

func (_this *ModbusAction) ReadOnce(addr, command uint8, regNo uint16, regLength uint8) error {
	//_this.sendFrame.Direction = Send
	//_this.receiveFrame.Direction = Receive
	if _this.writer == nil {
		return errors.New("写入流的方法为空")
	}
	if _this.reader == nil {
		return errors.New("读取流的方法为空")
	}
	_this.sendFrame.Addr = addr
	_this.sendFrame.Command = CommandType(command)
	_this.sendFrame.RegNo = regNo
	_this.sendFrame.RegLength = regLength
	sendbuffer, err := _this.sendFrame.analyzeStruct()
	if err != nil {
		return err
	}
	//DispArray(sendbuffer, "send:")
	err = (*_this.writer)(sendbuffer)
	if err != nil {
		return err
	}
	receiveBuffer, err := (*_this.reader)()
	if err != nil {
		return err
	}
	_this.receiveFrame.Addr = _this.sendFrame.Addr
	_this.receiveFrame.Command = _this.sendFrame.Command
	_this.receiveFrame.RegLength = _this.sendFrame.RegLength
	err = _this.receiveFrame.analyzeBytes(receiveBuffer, Receive)
	//DispArray(receiveBuffer, "receive:")
	if err != nil {
		return err
	}
	return nil
}

func (_this *ModbusAction) WriteOnce(addr, command uint8, regNo uint16, buffer []uint16) error {
	//_this.sendFrame.Direction = Send
	//_this.receiveFrame.Direction = Receive
	if _this.writer == nil {
		return errors.New("写入流的方法为空")
	}
	if _this.reader == nil {
		return errors.New("读取流的方法为空")
	}
	_this.sendFrame.Addr = addr
	_this.sendFrame.Command = CommandType(command)
	_this.sendFrame.RegNo = regNo
	_this.sendFrame.Body = buffer
	sendbuffer, err := _this.sendFrame.analyzeStruct()
	if err != nil {
		return err
	}
	//DispArray(sendbuffer, "send:")
	err = (*_this.writer)(sendbuffer)
	if err != nil {
		return err
	}
	receiveBuffer, err := (*_this.reader)()
	if err != nil {
		return err
	}
	_this.receiveFrame.Addr = _this.sendFrame.Addr
	_this.receiveFrame.Command = _this.sendFrame.Command
	_this.receiveFrame.RegLength = _this.sendFrame.RegLength
	err = _this.receiveFrame.analyzeBytes(receiveBuffer, Receive)
	//DispArray(receiveBuffer, "receive:")
	if err != nil {
		return err
	}
	return nil
}
